#include "server.hh"

#include <utility>

#include "listener.hh"

Server::Server (Database* db) :
  ios_ (),
  acc_ (ios_, tcp::endpoint (tcp::v4 (), 7171)),
  cl_ (),
  db_ (db),
  l_ (new Listener (this)),
  objvect_ (new ObjVect ()),
  nbclients_ (0),
  gt_ (new RtsThread (this, cl_, objvect_)),
  gm_ (new GameManager ())
{
  SDL_Init(SDL_INIT_TIMER);
}

Server::~Server ()
{
  delete l_;
  delete objvect_;
}

void
Server::init_map ()
{
  std::cout << "No config file, a flat 512 * 512 map will be generated." << std::endl << std::endl;
  std::cout << "***** Starting map initialization *****" << std::endl;
  OctreeRts* oct = OctreeRts::Instance (512);
  Map* map = new Map (512, 512);

  oct->insert_map (map);
  delete map;
  std::cout << "***** Map initialization done *****" << std::endl;
  
}

void Server::start ()
{
  //if wanna create a new thread
  //  boost::thread t (&RtsThread::run, &rts);
  //otherwise run into this thread
  gt_->run ();
}

void Server::init_rules (std::string filename)
{
  ConfParser* cp = new ConfParser (objvect_);

  cp->parse (filename, gm_);

  delete cp;

  start ();
}

void
Server::remove_client (int clientid)
{
  std::cout << "Client " << clientid << " disconected" << std::endl;

  cl_.erase (clientid);
  nbclients_--;
}

void
Server::new_client (boost::shared_ptr<Client> c)
{
  int i = nbclients_;
  tbb::concurrent_hash_map<int, boost::shared_ptr<Client> >::accessor acc;
  while (i >= 0)
  {
    if (!cl_.find (acc, nbclients_))
      break;

    --i;
  }

  if (i < 0)
  {
    i = nbclients_;
    while (cl_.find (acc, i))
      i++;
  }

  std::cout << "Client " << i << " connected, sending him information..." << std::endl;
  
  send_objects (c);
  send_zones (c);

  //adding client to client's list so it can receive requests
  cl_.insert (std::make_pair<int, boost::shared_ptr<Client> > (i, c));
  c->id (i);

  nbclients_++;
}

void
Server::send_objects (const boost::shared_ptr<Client>& c)
{
  for (ObjVect::iterator it = objvect_->begin ();
       it != objvect_->end (); ++it)
    c->nm ()->send (RType::GET_OBJECT, it->second->tonet ());
}

void
Server::send_zones (const boost::shared_ptr<Client>& c)
{
  for (tbb::concurrent_hash_map<FpsZone*, FpsThread*>::const_iterator it = fps_games_.begin ();
       it != fps_games_.end (); ++it)
    c->nm ()->send (RType::GAME_ZONE, it->first->tonet ());
}

void
Server::add_zone (FpsZone* z)
{
  fps_games_.insert (std::make_pair<FpsZone*, FpsThread*> (z, 0));
}

void
Server::ask_fps (FpsZone* z, int playerid)
{
  tbb::concurrent_hash_map<FpsZone*, FpsThread*>::accessor acc1;
  tbb::concurrent_hash_map<int, boost::shared_ptr<Client> >::accessor acc;

  if (!cl_.find (acc, playerid))
  {
    std::cout << "ERROR: could not put client " << playerid << " into fps mode" << std::endl;
    return;
  }

  //if the client is already in fps mode, don't update it again
  if (acc->second->mode () == GameMode::Fps)
    return;

  FpsThread* fpsthread = 0;

  assert (fps_games_.find (acc1, z));

  if (acc1->second)
  {
    std::cout << "Fps Thread already exist" << std::endl;
    fpsthread = acc1->second;
  }
  else
  {
    std::cout << "Creating new FPS Thread" << std::endl;
    fpsthread = new FpsThread (this, cl_, new ObjVect (), acc);
    acc1->second = fpsthread;
  }

  assert (fpsthread != 0);

  //link the fps thread to the client
  acc->second->locked (true);
  acc->second->fps_thread (fpsthread);
  //add the player to the mode
  //we now tell the client he is in fpsmode
  std::string tosend;
  tosend += (char) GameMode::Fps;
  acc->second->nm ()->send (RType::GAME_MODE, tosend);
  fpsthread->add_player (acc->first, acc->second);
}

void
Server::move_obj (Object* obj, FpsZone* z)
{
  OctreeRts::Instance ()->delete_obj (obj);
  objvect_->del (obj);

  tbb::concurrent_hash_map<FpsZone*, FpsThread*>::accessor acc;

  assert (fps_games_.find (acc, z));
  assert (acc->second);

  acc->second->add_obj (obj);
}
